//
// Created by TongHua on 2020/12/30.
//

#ifndef WEBSERVER_BLOCKQUEUE_H
#define WEBSERVER_BLOCKQUEUE_H

#include <pthread.h>
#include <sys/time.h>
#include <stdlib.h>
#include <iostream>
#include "../Lock/Locker.hpp"

/**
 * 阻塞队列
 * m_back = (m_back + 1) % m_max_size
 * 基于循环数组
 */
template < typename T>
class BlockQueue
{
public:
    BlockQueue( int _max_size = 1000 ) : m_max_size( _max_size )
    {
        if ( m_max_size < 0 )
        {
            exit( -1 );
        }
        m_array = new T[ m_max_size ];
        m_size = 0;
        m_front = -1;
        m_back = -1;
    }
    ~BlockQueue()
    {
        m_mutex.Lock();
        if ( m_array != NULL )
        {
            delete[] m_array;
        }
        m_mutex.UnLock();
    }
    /***
     * 阻塞队列是否已满
     * @return
     */
    bool IsFull()
    {
        m_mutex.Lock();
        if ( m_size >= m_max_size )
        {
            m_mutex.UnLock();
            return true;
        }
        m_mutex.UnLock();
        return false;
    }

    /***
     * 阻塞队列是否为空
     * @return
     */
    bool IsEmpty()
    {
        m_mutex.Lock();
        if ( m_size == 0 )
        {
            m_mutex.UnLock();
            return true;
        }
        m_mutex.UnLock();
        return false;
    }

    /**
     * 返回队首元素
     * @param value
     * @return
     */
    bool Front( T& value )
    {
        m_mutex.Lock();
        if ( 0 == m_size )
        {
            m_mutex.UnLock();
            return false;
        }
        value = m_array[ 0 ];
        m_mutex.UnLock();
        return true;
    }

    /**
     * 返回
     * @param value
     * @return
     */
    bool Back( T& value )
    {
        m_mutex.Lock();
        if ( 0 == m_size )
        {
            m_mutex.UnLock();
            return false;
        }
        value = m_array[ m_back ];
        m_mutex.UnLock();
        return true;
    }

    /**
     * 队列大小
     * @return
     */
    int Size()
    {
        int tmp = 0;
        m_mutex.Lock();
        tmp = m_size;
        m_mutex.UnLock();
        return tmp;
    }

    /**
     * 队列容量
     * @return
     */
    int MaxSize()
    {
        int tmp = 0;
        m_mutex.Lock();
        tmp = m_max_size;
        m_mutex.UnLock();
        return tmp;
    }

    /**
     * 插入元素至队列中
     * @param _item
     * @return
     */
    bool Push( const T& _item )
    {
        m_mutex.Lock();
        if ( m_size >= m_max_size )
        {
            m_cond.Broadcast();
            m_mutex.UnLock();
            return false;
        }
        m_back = ( m_back + 1 ) % m_max_size;
        m_array[ m_back ] = _item;

        m_size++;
        m_cond.Broadcast();
        m_mutex.UnLock();
        return true;
    }

    /**
     * 取出队首元素
     * @param _value
     * @return
     */
    bool Pop(T& _value)
    {
        m_mutex.Lock();
        while ( m_size <= 0 )   // 考虑到调用延时，如果有线程先一步取出最后的元素，那么后来的线程就需要再次等待新的元素添加进来
        {
            if ( !m_cond.Wait( m_mutex.Get() ) )
            {
                m_mutex.UnLock();
                return false;
            }
        }
        m_front = ( m_front + 1 ) % m_max_size;
        _value = m_array[ m_front ];
        m_size--;
        m_mutex.UnLock();
        return true;
    }

    /**
     * 取出队首元素
     * @param _value
     * @param ms_timeout
     * @return
     */
    bool Pop( T& _value, int ms_timeout )
    {
        timespec t = { 0, 0 };
        timeval now = { 0, 0 };
        gettimeofday( &now, NULL );
        m_mutex.Lock();
        if ( m_size <= 0 ) {
            t.tv_sec = now.tv_sec + ms_timeout / 1000;
            t.tv_nsec = (ms_timeout % 1000) * 1000;
            if ( ! m_cond.TimeWait( m_mutex.Get(), t ) )
            {
                m_mutex.UnLock();
                return false;
            }
        }
        if ( m_size <= 0 )  // 防止延时，导致有线程先一步取出所有的元素，由于加了timeout，所以直接返回false
        {
            m_mutex.UnLock();
            return false;
        }
        m_front = ( m_front + 1 ) % m_max_size;
        _value = m_array[ m_front ];
        m_size--;
        m_mutex.UnLock();
        return true;
    }

private:
    Locker m_mutex;
    Cond m_cond;

    T* m_array;
    int m_size;
    int m_max_size;
    int m_front;
    int m_back;
};

#endif //WEBSERVER_BLOCKQUEUE_H
